﻿/*
 * Copyright @ JuCheap All rights reserved.
 * 
 * 功能描述:仓储基类(包含单表的增、删、改、查、分页)
 * 作者: JuCheap
 * 时间: 04/02/2019 11:26:34
 */

using JuCheap.Core.Data;
using JuCheap.Core.Data.Entity;
using JuCheap.Core.Infrastructure;
using JuCheap.Core.Infrastructure.Exceptions;
using JuCheap.Core.Infrastructure.Extentions;
using JuCheap.Core.Infrastructure.Utilities;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;

namespace JuCheap.Core.Repository
{
    /// <summary>
    /// 仓储基类
    /// </summary>
    /// <typeparam name="TEntity">实体类型</typeparam>
    public class RepositoryBase<TEntity> where TEntity : class, IBaseEntity, new()
    {
        private readonly ICurrentUserService _currentUserService;
        private readonly JuCheapContext _context;

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="context"></param>
        /// <param name="currentUserService"></param>
        public RepositoryBase(JuCheapContext context, ICurrentUserService currentUserService)
        {
            _context = context;
            _currentUserService = currentUserService;
        }

        /// <summary>
        /// Queryable(如果是不是系统公司，会自动加上CompanyId过滤参数)
        /// </summary>
        /// <param name="enableCompanyFilter">是否自动加上公司Id过滤参数(默认为true)</param>
        /// <returns></returns>
        public IQueryable<TEntity> Queryable(bool enableCompanyFilter = true)
        {
            var companyId = _currentUserService.GetCompanyId();
            return _context.Set<TEntity>().WhereIf(enableCompanyFilter && companyId.IsNotBlank(), x => x.CompanyId == companyId);
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="entity">实体</param>
        /// <returns></returns>
        public async Task<string> InsertAsync(TEntity entity)
        {
            var user = _currentUserService.GetCurrentUser();
            entity.Create(user);
            await _context.AddAsync(entity);
            await _context.SaveChangesAsync();
            return entity.Id;
        }

        /// <summary>
        /// 批量添加
        /// </summary>
        /// <param name="list">实体集合</param>
        public async Task BatchInsertAsync(List<TEntity> list)
        {
            var user = _currentUserService.GetCurrentUser();
            foreach (var entity in list)
            {
                entity.Create(user);
            }
            await _context.AddRangeAsync(list);
            await _context.SaveChangesAsync();
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="properties">需要更新字段的表达式(可以不指定，默认全部更新)</param>
        /// <returns></returns>
        public async Task UpdateAsync(TEntity entity, params Expression<Func<TEntity, object>>[] properties)
        {
            var user = _currentUserService.GetCurrentUser();
            entity.Modify(user);
            var dbEntityEntry = _context.Entry(entity);
            if (properties.AnyOne())
            {
                foreach (var property in properties)
                {
                    dbEntityEntry.Property(property).IsModified = true;
                }
                //修改时间和修改人，默认需要更新
                dbEntityEntry.Property(x => x.ModifyTime).IsModified = true;
                dbEntityEntry.Property(x => x.ModifyUserId).IsModified = true;
            }
            else
            {
                var notUpdateColumns = new List<string>
                {
                    "Id",
                    "CreateTime",
                    "CreateUserId",
                    "CompanyId"
                };
                foreach (var rawProperty in dbEntityEntry.Entity.GetType().GetTypeInfo().DeclaredProperties)
                {
                    if (!notUpdateColumns.Contains(rawProperty.Name))
                    {
                        dbEntityEntry.Property(rawProperty.Name).IsModified = true;
                    }
                }
            }
            await _context.SaveChangesAsync();
        }

        /// <summary>
        /// 删除指定主键的数据(逻辑删除)
        /// </summary>
        /// <param name="keyId">主键Id</param>
        /// <returns></returns>
        public async Task DeleteAsync(string keyId)
        {
            if (keyId.IsBlank())
            {
                return;
            }
            var user = _currentUserService.GetCurrentUser();
            var entity = new TEntity() { Id = keyId };
            entity.Delete(user);
            var dbEntityEntry = _context.Entry(entity);
            dbEntityEntry.Property(x => x.IsDeleted).IsModified = true;
            dbEntityEntry.Property(x => x.ModifyTime).IsModified = true;
            dbEntityEntry.Property(x => x.ModifyUserId).IsModified = true;
            await _context.SaveChangesAsync();
        }

        /// <summary>
        /// 批量删除(逻辑删除)
        /// </summary>
        /// <param name="keyIds">主键Id集合</param>
        /// <returns></returns>
        public async Task DeleteAsync(IList<string> keyIds)
        {
            if (keyIds.AnyOne())
            {
                var user = _currentUserService.GetCurrentUser();
                var entityList = await Queryable().Where(x => keyIds.Contains(x.Id)).ToListAsync();
                foreach (var entity in entityList)
                {
                    entity?.Delete(user);
                }
                await _context.SaveChangesAsync();
            }
        }

        /// <summary>
        /// 根据条件删除实体(物理删除)
        /// </summary>
        /// <param name="predicate">条件表达式</param>
        /// <returns></returns>
        public async Task<bool> DeleteAsync(Expression<Func<TEntity, bool>> predicate)
        {
            var entityList = await Queryable().Where(predicate).ToListAsync();
            _context.RemoveRange(entityList);
            await _context.SaveChangesAsync();
            return true;
        }

        /// <summary>
        /// 根据主键查询实体，如果没有找到，则抛出异常信息
        /// </summary>
        /// <param name="keyValue">主键</param>
        /// <returns></returns>
        public async Task<TEntity> FindAsync(object keyValue)
        {
            var entity = await _context.Set<TEntity>().FindAsync(keyValue);
            if (entity == null)
            {
                throw new BusinessException("没有找到对应的数据");
            }
            var user = _currentUserService.GetCurrentUser();
            var isNotSystemCompany = user?.IsSystemCompany == false;
            var companyId = _currentUserService.GetCompanyId();
            if(isNotSystemCompany && companyId.IsNotBlank() && !entity.CompanyId.IsEqual(companyId))
            {
                throw new BusinessException("您无权限访问不属于您的数据");
            }
            return entity;
        }

        /// <summary>
        /// 根据条件查询单个实体
        /// </summary>
        /// <param name="predicate">条件表达式</param>
        /// <returns></returns>
        public async Task<TEntity> FindAsync(Expression<Func<TEntity, bool>> predicate)
        {
            return await Queryable().FirstOrDefaultAsync(predicate);
        }

        /// <summary>
        /// 根据条件判断是否存在相同的数据
        /// </summary>
        /// <param name="predicate">条件表达式</param>
        /// <returns></returns>
        public async Task<bool> ExistsAsync(Expression<Func<TEntity, bool>> predicate)
        {
            return await Queryable().AnyAsync(predicate);
        }

        /// <summary>
        /// 基础单表分页查询
        /// </summary>
        /// <param name="filter">分页条件</param>
        /// <param name="predicate">条件表达式</param>
        /// <returns></returns>
        public async Task<PagedResult<TEntity>> QueryPagesAsync(BaseCondition filter, Expression<Func<TEntity, bool>> predicate)
        {
            if (filter.PageIndex <= 0)
                throw new ArgumentException("当前页码必须大于0 !", "pageIndex");
            if (filter.PageSize <= 1)
                throw new ArgumentException("每页显示条数必须大于0 !", "pageSize");
            if (filter.PageSize > 10000)
                throw new BusinessException("每页显示数据最大条数不能超过1万条");

            var source = Queryable();

            var count = await source.WhereIf(predicate != null, predicate).CountAsync();
            var rows = new List<TEntity>();
            if (count > 0)
            {

                var pagedQuery = source.WhereIf(predicate != null, predicate)
                    .OrderBy(filter.SortField, filter.SortType)
                    .Skip((filter.PageIndex - 1) * filter.PageSize)
                    .Take(filter.PageSize);
                rows = await pagedQuery.ToListAsync();
            }

            return new PagedResult<TEntity>
            {
                Datas = rows,
                Records = count,
                PageIndex = filter.PageIndex,
                PageSize = filter.PageSize
            };
        }
    }
}
